/***************************************************************************
 *
 *   Copyright (C) 2012 by Ben Barsdell and Andrew Jameson
 *   Licensed under the Academic Free License version 2.1
 *
 ***************************************************************************/

#include <float.h>
#include <cmath>
#include <iostream>
#include <stdexcept>

using std::cerr;
using std::cout;
using std::endl;

#include "formats/SigprocFile.h"
#include "formats/header.h"

SigprocFile::SigprocFile(const char* filename, bool _fswap)
    : DataSource(), m_file_stream(filename, std::ios::binary)
{
    m_error = 0;
    fswap = _fswap;

    if (m_file_stream.fail()) {
        cerr << "ERROR: Failed to open file '" << filename << "'" << endl;
        m_error = -1;
    }

    SigprocHeader m_header;
    read_header(m_file_stream, m_header);
    if (m_file_stream.fail()) {
        cerr << "ERROR: Failed to read from file '" << filename << "'" << endl;
        m_error = -2;
    }

    nchan = m_header.nchans;
    nbit = m_header.nbits;
    beam = m_header.ibeam;
    tsamp = m_header.tsamp; // in seconds
    spectra_rate = 1 / tsamp;

    f0 = m_header.fch1;
    df = m_header.foff;

    utc_start = mjd2utctm(m_header.tstart);
    int buffer_size = 64;
    char buffer[buffer_size];
    strftime(buffer, buffer_size, HD_TIMESTR, localtime(&utc_start));

    stride = (nchan * nbit) / (8 * sizeof(char));

#ifdef _DEBUG
    std::cerr << "SigprocFile::SigprocFile nchan=" << nchan << std::endl;
    std::cerr << "SigprocFile::SigprocFile nbit=" << nbit << std::endl;
    std::cerr << "SigprocFile::SigprocFile beam=" << beam << std::endl;
    std::cerr << "SigprocFile::SigprocFile tsamp=" << tsamp << std::endl;
    std::cerr << "SigprocFile::SigprocFile spectra_rate=" << spectra_rate << std::endl;
    std::cerr << "SigprocFile::SigprocFile f0=" << f0 << std::endl;
    std::cerr << "SigprocFile::SigprocFile df=" << df << std::endl;
#endif

    first_time = true;
    offset = 0;
    scale = 1;
}

SigprocFile::~SigprocFile() { m_file_stream.close(); }

size_t SigprocFile::get_data(size_t nsamps, char* data)
{
    if (this->get_error()) {
        return 0;
    }
    size_t nchan_bytes = stride;
    m_file_stream.read((char*)&data[0], nsamps * nchan_bytes);

    // by default sigproc 32-bit data are stored as floats, dedisp requires unsigned ints
    if (nbit == 32) {
        const unsigned nfloats = nsamps * nchan;
        unsigned* out = (unsigned*)&data[0];
        float* in = (float*)&data[0];

        if (first_time) {
            float sum = 0;
            float min_float = FLT_MAX;
            float max_float = -FLT_MAX;
            for (unsigned i = 0; i < nfloats; i++) {
                if (in[i] < min_float)
                    min_float = in[i];
                if (in[i] > max_float)
                    max_float = in[i];
                sum += in[i];
            }
            float mean = sum / nfloats;

            offset = powf(2, 31) - mean;
            scale = (max_float - min_float) / powf(2, 28);
            first_time = false;
        }

        for (unsigned i = 0; i < nfloats; i++) {
            out[i] = (unsigned int)((in[i] / scale) + offset);
        }
    }

    if (fswap) {
        if (nbit == 2 || nbit == 4 || nbit == 8) {
            unsigned samps_per_char = 8 / nbit;
            char* inptr = data;
            unsigned nchar_per_sample = nchan / samps_per_char;
            for (unsigned i = 0; i < nsamps; i++) {
                for (unsigned idat = 0; idat < (nchar_per_sample / 2); idat++) {
                    unsigned odat = nchar_per_sample - (1 + idat);
                    unsigned char this_dat = inptr[idat];
                    unsigned char that_dat = inptr[odat];

                    if (nbit == 2) {
                        this_dat = flip_2bit(this_dat);
                        that_dat = flip_2bit(that_dat);
                    }
                    if (nbit == 4) {
                        this_dat = flip_4bit(this_dat);
                        that_dat = flip_4bit(that_dat);
                    }

                    inptr[odat] = this_dat;
                    inptr[idat] = that_dat;
                }
                inptr += nchar_per_sample;
            }
        } else
            throw std::runtime_error("Could not FSWAP on the input bitrate");
    }

    size_t bytes_read = m_file_stream.gcount();
    return bytes_read / nchan_bytes;
};
